package cn.ava.ymcc.service.impl;

import cn.ava.ymcc.domain.*;
import cn.ava.ymcc.dto.LoginTokenInfoDto;
import cn.ava.ymcc.feign.UaaFeignClient;
import cn.ava.ymcc.constants.CommonConstants;
import cn.ava.ymcc.mapper.UserRecommendMapper;
import cn.ava.ymcc.result.JSONResult;
import cn.ava.ymcc.utils.AssertUtil;
import cn.ava.ymcc.utils.BitStatesUtils;
import cn.ava.ymcc.dto.RedisSMSCodeValueDTO;
import cn.ava.ymcc.dto.UserRegisterDto;
import cn.ava.ymcc.mapper.UserAccountMapper;
import cn.ava.ymcc.mapper.UserBaseInfoMapper;
import cn.ava.ymcc.mapper.UserMapper;
import cn.ava.ymcc.service.IUserService;
import cn.ava.ymcc.utils.LoginInfoContextUtil;
import cn.ava.ymcc.utils.StrUtils;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.mapper.Wrapper;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.List;

/**
 * <p>
 * 会员登录账号 服务实现类
 * </p>
 *
 * @author ava
 * @since 2022-08-04
 */
@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private UserAccountMapper userAccountMapper;

    @Autowired
    private UserBaseInfoMapper userBaseInfoMapper;

    @Autowired
    private UserRecommendMapper userRecommendMapper;

    @Autowired
    private RedisTemplate<Object,Object> redisTemplate;

    @Autowired
    private UaaFeignClient uaaFeignClient;

    private PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();

    /**
     * @Title: register
     * @Description: 用户注册
     * @Author: Avavaava
     * @Version: 23.53
     * @Date: 2022/8/9 11:39
     * @param:
     * @return:
     */
    @Override
    @GlobalTransactional(rollbackFor = Exception.class)
    @Transactional
    public JSONResult register(UserRegisterDto userRegisterDto) {
        // 校验用户是否存在
        isUserEmpty(userRegisterDto.getMobile());
        // 校验是否存在推荐码
        UserRecommend userRecommend = new UserRecommend();
        if (!StringUtils.isBlank(userRegisterDto.getRecommendCode())){
            // 如果存在,根据推荐码校验用户是否正确
            userRecommend.setCode(userRegisterDto.getRecommendCode());
            userRecommend = userRecommendMapper.selectOne(userRecommend);
            AssertUtil.isNotNull(userRecommend,"推荐码不存在!");
        }
        // 拼接业务键
        String redisSmsCodeKey = CommonConstants.UserConstant.BUSINESS_REGISTER_KEY + userRegisterDto.getMobile();
        // 校验短信验证码
        checkVerifyCode(redisSmsCodeKey,userRegisterDto.getSmsCode());
        // 保存user相关表和login表
        // 初始化longin表并返回主键
        Long loginId = initLoginAndReturnId(userRegisterDto);
        // 初始化user表保存loginId
        User user = initUserAndReturnUser(userRegisterDto, loginId);
        // 初始化user基本信息表
        initUserBaseInfo(user,userRegisterDto,userRecommend.getUserId());
        // 初始化user账户表
        initUserAccount(user);
        // 初始化用户推荐表
        initUserRecommend(user.getId());
        // 删除redis中的验证码数据
        deleteRedisData(redisSmsCodeKey);
        return JSONResult.success();
    }

    /**
     * 根据登录人查找推荐码
     * @return
     */
    @Override
    public UserRecommend recommendCode() {
        LoginTokenInfoDto loginContext = LoginInfoContextUtil.getLoginContext();
        UserRecommend userRecommend = new UserRecommend();
        userRecommend.setUserId(loginContext.getLoginId());
        userRecommend = userRecommendMapper.selectOne(userRecommend);
        return userRecommend;
    }

    /**
     * 添加用户推荐码
     * @param UserId
     */
     void initUserRecommend(Long UserId) {
         // 查询出总条数
         List<UserRecommend> userRecommends = userRecommendMapper.selectList(null);
         // 推荐码长度
         int length = 6;
         // 总条数
         int count = userRecommends.size();
         // 生成随机推荐码
         String code = StrUtils.getComplexRandomString(length);
         // 循环判断,此推荐码是否被占用
         UserRecommend userRecommend = new UserRecommend();
         Wrapper<UserRecommend> query = new EntityWrapper<>();
         query.eq("code",code);
         List<UserRecommend> recommends = userRecommendMapper.selectList(query);
         while (!recommends.isEmpty()){
             recommends = userRecommendMapper.selectList(query);
             // 如果被占用,总条数自减
             count--;
             // 再次重新生成推荐码
             code = StrUtils.getComplexRandomString(length);
             userRecommend.setCode(code);
             // 当总条数自减到自身一半长度时,还没找到时,为了效率直接推荐码长度加1
             if (count ==  count / 2){
                 // 推荐码长度加1
                 length += 1;
             }
         }
         userRecommend.setCode(code);
         userRecommend.setUserId(UserId);
         userRecommendMapper.insert(userRecommend);
     }

    /**
     * @Title: isUserEmpty
     * @Description: 校验用户是否存在
     * @Author: Avavaava
     * @Version: 23.53
     * @Date: 2022/8/7 20:44
     * @param:
     * @return:
     */
    void isUserEmpty(String account) {
        // 根据手机号查询是否存在此用户
        Wrapper<User> wrapper = new EntityWrapper<>();
        wrapper.eq("phone", account);
        List<User> userList = userMapper.selectList(wrapper);
        // 判断账号是否存在
        AssertUtil.isTrue(userList.isEmpty(),"账号已经存在,请直接登录!");
    }


    /**
     * @Title: checkVerifyCode
     * @Description: 校验验证码
     * @Author: Avavaava
     * @Version: 23.53
     * @Date: 2022/8/7 18:47
     * @param:
     * @return:
     */
    void checkVerifyCode(String redisKey, String code) {
        // 查询出验证码是否存在
        RedisSMSCodeValueDTO redisSmsCodeValue = (RedisSMSCodeValueDTO)redisTemplate.opsForValue().get(redisKey);
        AssertUtil.isNotNull(redisSmsCodeValue,"验证码不存在,请重新获取!");
        // 再判断是否正确
        AssertUtil.isEqualsIgnoreCase(redisSmsCodeValue.getCode(),code,"验证码错误!");
    }

    /**
     * @Title: initLogin
     * @Description: 初始化登录表并返回id
     * @Author: Avavaava
     * @Version: 23.53
     * @Date: 2022/8/7 2:35
     * @param:
     * @return:
     */
    @Transactional
     Long initLoginAndReturnId(UserRegisterDto UserRegisterDto) {
        Login login = new Login();
        login.setUsername(UserRegisterDto.getMobile());
        login.setPassword(passwordEncoder.encode(UserRegisterDto.getPassword()));
        login.setType(UserRegisterDto.getRegChannel());
        login.setEnabled(true);
        JSONResult jsonResult = uaaFeignClient.saveOrUpdate(login);
        login = JSONObject.parseObject(JSONObject.toJSONString(jsonResult.getData()), Login.class);
        return login.getId();
    }

    
    /**
     * 插入user表  并返回user对象
     * @param dto
     * @param loginId
     * @return
     */
    User initUserAndReturnUser(UserRegisterDto dto , Long loginId){
        User user = new User();
        user.setPhone(dto.getMobile());
        user.setCreateTime(new Date().getTime());
        user.setNickName(dto.getMobile());
        Long states = BitStatesUtils.batchAddState(0L,
                BitStatesUtils.OP_REGISTED,
                BitStatesUtils.OP_ACTIVED,
                BitStatesUtils.OP_AUTHED_PHONE
        );
        user.setBitState(states);
        user.setLoginId(loginId);
        userMapper.insert(user);
        return user;
    }


    /**
     * 插入user base info 表
     * @param user
     * @param dto
     */
    void initUserBaseInfo(User user , UserRegisterDto dto,Long recommenderId){
        UserBaseInfo userBaseInfo = new UserBaseInfo();
        userBaseInfo.setId(user.getId());
        userBaseInfo.setCreateTime(user.getCreateTime());
        userBaseInfo.setRegChannel(dto.getRegChannel());
        // 设置推荐人id
        userBaseInfo.setReferId(recommenderId);
        userBaseInfoMapper.insert(userBaseInfo);
    }


    /**
     * 插入user account 表
     * @param user
     */
    void initUserAccount(User user){
        UserAccount userAccount = new UserAccount();
        userAccount.setId(user.getId());
        userAccount.setCreateTime(user.getCreateTime());
        userAccountMapper.insert(userAccount);
    }

    /**
     * @Title: deleteRedisData
     * @Description: 删除redis中的数据
     * @Author: Avavaava
     * @Version: 23.53
     * @Date: 2022/8/9 11:38
     * @param:
     * @return:
     */
    void deleteRedisData(String... redisKey) {
        for (String key : redisKey) {
            redisTemplate.delete(key);
        }
    }

}
